package org.zzk.handler;

import com.google.gson.JsonObject;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zzk.dao.*;
import org.zzk.exception.GameException;

import org.zzk.dao.CommandType;

@ChannelHandler.Sharable
public class ChessMoveHandler extends SimpleChannelInboundHandler<ChessPacket> {
    public static final ChessMoveHandler INSTANCE = new ChessMoveHandler();
    private static final RoomManager roomManager = RoomManager.getInstance();
    private static final Logger logger = LoggerFactory.getLogger(ChessMoveHandler.class);

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ChessPacket packet) {
        logger.info("start move");
        if (packet.getCommand() != CommandType.MOVE) { // 非棋步 指令
            ctx.fireChannelRead(packet);
            return;
        }

        JsonObject data = packet.getData();
        String roomId = data.get("room_id").getAsString();
        String playerId = data.get("player_id").getAsString();
        String action = data.get("action").getAsString();
        JsonObject move = data.getAsJsonObject("move");

        try {
            // 1. 验证房间和玩家
            Room room = roomManager.getRoom(roomId)
                    .orElseThrow(() -> new GameException(
                            GameException.ErrorType.ROOM_NOT_FOUND,
                            "房间不存在"));

            logger.info("房间id - [{}]", roomId);

            Player currentPlayer = room.getCurrentPlayer()
                    .orElseThrow(() -> new GameException(
                            GameException.ErrorType.CURRENT_NOT_HAVE_PLAYER,
                            "当前无活动玩家"));

            logger.info("current Player id - [{}]", currentPlayer.getId());
            logger.info("Player id - [{}]", playerId);

            if (!currentPlayer.getId().equals(playerId)) {
                throw new GameException(
                        GameException.ErrorType.WRONG_TURN,
                        "不是你的回合");
            }

            logger.info("start parseMove");
            ChessMove chessMove = parseMove(move);
            ChessMove chessMoveOpponent = parseMoveOpponent(move);
            logger.info("end parseMove");

            logger.info("action={}", action);
            if (!action.equals("move")) {
                broadcastSelect(room, chessMoveOpponent, playerId);
                return;
            }

            logger.info("start makeMove");
            // 2. 执行移动逻辑
            //GameResult result = room.makeMove(chessMove);
            logger.info("end makeMove");

            // 3. 广播棋步
            broadcastMove(room, chessMoveOpponent, playerId);

            room.setCurrentPlayer();
            // 4. 处理游戏结束
//            if (result.isGameOver()) {
//                broadcastGameEnd(room, result);
//                roomManager.removeRoom(roomId);
//            }

        } catch (GameException e) {
            sendError(ctx, playerId, e.getMessage());
        }
    }

    private ChessMove parseMove(JsonObject moveJson) {
        return new ChessMove(
                moveJson.get("from_x").getAsInt(),
                moveJson.get("from_y").getAsInt(),
                moveJson.get("to_x").getAsInt(),
                moveJson.get("to_y").getAsInt()
        );
    }

    private ChessMove parseMoveOpponent(JsonObject moveJson) {
        int fromX = moveJson.get("from_x").getAsInt();
        fromX = (fromX == -1) ? -1 : (9 - fromX);
        int fromY = moveJson.get("from_y").getAsInt();
        fromY = (fromY == -1) ? -1 : (8 - fromY);
        int toX = moveJson.get("to_x").getAsInt();
        toX = (toX == -1) ? -1 : (9 - toX);
        int toY = moveJson.get("to_y").getAsInt();
        toY = (toY == -1) ? -1 : (8 - toY);
        return new ChessMove(fromX, fromY, toX, toY);
    }

    private void broadcastSelect(Room room, ChessMove chessMoveOpponent, String playerId) {
        logger.info("broadcastSelect");
        JsonObject broadcast = new JsonObject();
        broadcast.addProperty("room_id", room.getId());
        broadcast.addProperty("player_id", playerId);
        broadcast.addProperty("action", "select");
        broadcast.add("move", chessMoveOpponent.toJson());

        logger.info("broadcastSelect - [{}]", broadcast.toString());

        if (room.getOpponentPlayer().isPresent()) {
            room.getOpponentPlayer().get().sendPacket(new ChessPacket(CommandType.MOVE, broadcast));
        }
    }

    private void broadcastMove(Room room, ChessMove chessMoveOpponent, String playerId) {
        JsonObject broadcast = new JsonObject();
        broadcast.addProperty("room_id", room.getId());
        broadcast.addProperty("player_id", playerId);
        broadcast.addProperty("action", "move");
        broadcast.add("move", chessMoveOpponent.toJson());

        logger.info("broadcastMove - [{}]", broadcast.toString());

        if (room.getOpponentPlayer().isPresent()) {
            room.getOpponentPlayer().get().sendPacket(new ChessPacket(CommandType.MOVE, broadcast));
        }
    }

    private void broadcastGameEnd(Room room, GameResult result) {
        JsonObject endMsg = new JsonObject();
        endMsg.addProperty("type", "game_over");
        endMsg.addProperty("winner", result.getWinner());
        endMsg.addProperty("reason", result.getReason());

        room.getPlayers().forEach(player ->
                player.sendPacket(new ChessPacket(CommandType.GAME_OVER, endMsg))
        );
    }

    private void sendError(ChannelHandlerContext ctx, String playerId, String error) {
        JsonObject errorMsg = new JsonObject();
        errorMsg.addProperty("player_id", playerId);
        errorMsg.addProperty("error", error);
        ctx.writeAndFlush(new ChessPacket(CommandType.ERROR, errorMsg));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        JsonObject exceptionMsg = new JsonObject();
        exceptionMsg.addProperty("error", "服务器内部错误");
        ctx.writeAndFlush(new ChessPacket(CommandType.ERROR, exceptionMsg));
        ctx.close();
    }
}
